﻿using System;
using System.Collections.Generic;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text.RegularExpressions;
using UFIDA.U9.WSS.ProxyService.Exceptions;
using UFIDA.U9.WSS.ProxyService.Interfaces.Interfaces;
using UFIDA.U9.WSS.ProxyService.Json;
using UFIDA.U9.WSS.ProxyService.Models;
using UFIDA.U9.WSS.ProxyService.Utils;
using UFSoft.UBF;
using UFSoft.UBF.Business;
using UFSoft.UBF.Util.Log;

namespace UFIDA.U9.WSS.ProxyService.Behavior.WebHttp.Base
{
    public class ExtendErrorHandler : IErrorHandler
    {
        private static readonly ILogger Logger = LoggerManager.GetLogger("ExtendErrorHandler");
        private readonly ServiceEndpoint _endpoint;
        private EndpointDispatcher _endpointDispatcher;

        /// <summary>
        ///     构造器
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="endpointDispatcher"></param>
        public ExtendErrorHandler(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            _endpoint = endpoint;
            _endpointDispatcher = endpointDispatcher;
        }

        public bool HandleError(Exception error)
        {
            Logger.Error("HandleError: {0}", error);
            return true;
        }

        public void ProvideFault(
            Exception error, MessageVersion version, ref Message fault)
        {
            //当错误时，构建返回的对象
            OperationDescription operationDescription = GetCurrentOperationDescription();
            IReturnMessage ret;
            if (operationDescription == null)
            {
                ret = new NoDataReturnMessage();
                //ret.IsSuccess = false;
                //ret.Code = ExceptionCodes.ServiceOperationIsNotExist;
                //ret.ErrMsg = "operation is not exist";
            }
            else
            {
                Type returnType = operationDescription.SyncMethod.ReturnType;
                ret = Activator.CreateInstance(returnType) as IReturnMessage ?? new NoDataReturnMessage();
            }
            ret.IsSuccess = false;
            ret.Code = GetExceptionCode(error);
            ret.ErrMsg = GetExceptionErrorMsg(error);
            string strResponeMessageIncludeStackInfo =
                ConfigurationHelper.GetAppSettingValue(Constant.ResponeMessageIncludeStackInfoName);
            bool responeMessageIncludeStackInfo = !string.IsNullOrEmpty(strResponeMessageIncludeStackInfo) &&
                                                  strResponeMessageIncludeStackInfo.ToLower() == "true";
            ret.StackString = responeMessageIncludeStackInfo
                ? StackTraceHelper.GetExceptionStackTraceString(error)
                : string.Empty;
            byte[] body = JsonHelper.GetReturnJsonBody(ret);
            fault = Message.CreateMessage(version, "",
                new RawBodyWriter(body));
            fault.Properties.Add(WebBodyFormatMessageProperty.Name,
                new WebBodyFormatMessageProperty(WebContentFormat.Raw));
            HttpResponseMessageProperty respProp = new HttpResponseMessageProperty();
            respProp.Headers[HttpResponseHeader.ContentType] = "application/json;charset=utf-8";
            // return custom error code, 200.
            respProp.StatusCode = HttpStatusCode.OK;
            //responseMessageProperty.StatusCode = HttpStatusCode.BadRequest;
            respProp.StatusDescription = "Bad request";
            fault.Properties.Add(HttpResponseMessageProperty.Name, respProp);
        }

        /// <summary>
        ///     获取异常代码
        /// </summary>
        /// <param name="error"></param>
        /// <returns></returns>
        private static int GetExceptionCode(Exception error)
        {
            WSException wsEx = error as WSException;
            if (wsEx != null) return wsEx.ExceptionCode;
            //U9的错误基类
            if (error is ExceptionBase) return ExceptionCodes.ProxyServiceCallFail;
            if (error.InnerException != null) return GetExceptionCode(error.InnerException);
            return ExceptionCodes.UnKnownException;
        }

        /// <summary>
        ///     获取错误信息
        /// </summary>
        /// <param name="ex"></param>
        /// <param name="isTop">顶层调用</param>
        /// <returns></returns>
        private static string GetExceptionErrorMsg(Exception ex, bool isTop = true)
        {
            List<string> errList = new List<string>();
            if (ex is WSException)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    errList.Add(ex.Message);
                if (ex.InnerException != null)
                    errList.Add(GetExceptionErrorMsg(ex.InnerException, false));
            }
            else if (ex is BusinessException)
            {
                BusinessException bex = ex as BusinessException;
                if (bex.InnerExceptions != null && bex.InnerExceptions.Count > 0)
                {
                    foreach (Exception iex in bex.InnerExceptions)
                    {
                        errList.Add(GetExceptionErrorMsg(iex, false));
                    }
                }
            }
            else
            {
                errList.Add(ex.InnerException == null ? ex.Message : ex.InnerException.Message);
            }

            string errMsg = string.Join(".", errList.ToArray());
            if (!isTop) return errMsg;
            //移除<a href></a>
            string hrefPattern =
                @"(?is)<a(?:(?!href=).)*href=(['""]?)(?<url>[^""\s>]*)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>";
            return Regex.Replace(errMsg, hrefPattern, string.Empty);
        }

        /// <summary>
        ///     获取当前的动作描述
        /// </summary>
        /// <returns></returns>
        private OperationDescription GetCurrentOperationDescription()
        {
            MessageHeaders incomingMessageHeaders = OperationContext.Current.IncomingMessageHeaders;
            string currentActionName = OperationContext.Current.IncomingMessageHeaders.Action;
            if (string.IsNullOrEmpty(currentActionName))
                currentActionName = incomingMessageHeaders.To.Segments[incomingMessageHeaders.To.Segments.Length - 1];
            if (string.IsNullOrEmpty(currentActionName)) return null;
            foreach (OperationDescription operationDescription in _endpoint.Contract.Operations)
            {
                if (string.Equals(operationDescription.Name, currentActionName,
                    StringComparison.CurrentCultureIgnoreCase)) return operationDescription;
            }

            return null;
        }
    }
}